package org.dromara.common.security.config

import cn.dev33.satoken.exception.NotLoginException
import cn.dev33.satoken.filter.SaServletFilter
import cn.dev33.satoken.`fun`.SaFunction
import cn.dev33.satoken.httpauth.basic.SaHttpBasicUtil
import cn.dev33.satoken.interceptor.SaInterceptor
import cn.dev33.satoken.router.SaRouter
import cn.dev33.satoken.stp.StpUtil
import cn.dev33.satoken.util.SaResult
import io.github.oshai.kotlinlogging.KotlinLogging
import org.dromara.common.core.constant.HttpStatus
import org.dromara.common.core.utils.ServletUtils.getParameter
import org.dromara.common.core.utils.ServletUtils.getRequest
import org.dromara.common.core.utils.SpringUtils
import org.dromara.common.core.utils.SpringUtils.getProperty
import org.dromara.common.core.utils.StringUtils
import org.dromara.common.satoken.utils.LoginHelper
import org.dromara.common.security.config.properties.SecurityProperties
import org.dromara.common.security.handler.AllUrlHandler
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.autoconfigure.AutoConfiguration
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.web.servlet.config.annotation.InterceptorRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

/**
 * 权限安全配置
 *
 * @author LikeYouDo
 * @date 2025/7/17
 */

@AutoConfiguration
@EnableConfigurationProperties(SecurityProperties::class)
class SecurityConfig(
    private val securityProperties: SecurityProperties
) : WebMvcConfigurer {

    companion object {
        private val log = KotlinLogging.logger { }
    }

    @Value("\${sse.path}")
    lateinit var ssePath: String

    /**
     * 注册sa-token的拦截器
     */
    override fun addInterceptors(registry: InterceptorRegistry) {
        // 注册路由拦截器，自定义验证规则
        registry.addInterceptor(SaInterceptor({ handler: Any ->
            val allUrlHandler: AllUrlHandler = SpringUtils.getBean(AllUrlHandler::class.java)
            // 登录验证 -- 排除多个路径
            SaRouter
                // 获取所有的
                .match(allUrlHandler.urls)
                // 对未排除的路径进行检查
                .check(SaFunction {
                    val request = getRequest()
                    // 检查是否登录 是否有token
                    StpUtil.checkLogin()

                    // 检查 header 与 param 里的 clientid 与 token 里的是否一致
                    val headerCid = request.getHeader(LoginHelper.CLIENT_KEY)
                    val paramCid = getParameter(LoginHelper.CLIENT_KEY)
                    val clientId: String = StpUtil.getExtra(LoginHelper.CLIENT_KEY).toString()
                    if (!StringUtils.equalsAny(clientId, headerCid, paramCid)) {
                        // token 无效
                        throw NotLoginException.newInstance(
                            StpUtil.getLoginType(),
                            "-100", "客户端ID与Token不匹配",
                            StpUtil.getTokenValue()
                        )
                    }

                    // 有效率影响 用于临时测试
                    // if (log.isDebugEnabled()) {
                    //     log.info("剩余有效时间: {}", StpUtil.getTokenTimeout());
                    //     log.info("临时有效时间: {}", StpUtil.getTokenActivityTimeout());
                    // }
                })
        })).addPathPatterns("/**")
            // 排除不需要拦截的路径
            .excludePathPatterns(*securityProperties.excludes)
            .excludePathPatterns(ssePath);
    }

    /**
     * 对 actuator 健康检查接口 做账号密码鉴权
     */
    @Bean
    fun getSaServletFilter(): SaServletFilter {
        val username = getProperty("spring.boot.admin.client.username")
        val password = getProperty("spring.boot.admin.client.password")
        return SaServletFilter()
            .addInclude("/actuator", "/actuator/**")
            .setAuth { obj: Any ->
                SaHttpBasicUtil.check("$username:$password")
            }
            .setError { e: Throwable ->
                SaResult.error(e.message).setCode(HttpStatus.UNAUTHORIZED)
            }
    }

}
